package ctp.statesgraph;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections15.Transformer;

import ctp.ctpgraph.CTPGraph;
import ctp.ctpgraph.Edge;
import ctp.ctpgraph.Vertex;
import ctp.utils.GraphUtils;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.graph.Graph;

public class Action implements Arc {
	
	List<Edge> path;
	double cost;
	Vertex destination;
	
	public Action(List<Edge> path, double cost, Vertex destination) {
		super();
		this.path = path;
		this.cost = cost;
		this.destination = destination;
	}

	public static Set<Action> buildActions(CTPGraph ctpGraph, ActionState actionState) {
		Set<Action> actions = new HashSet<Action>();
		
		// build a kernel graph K: all vertices such that all their incident edges are knows.
		// for every neighbor v of k (not part of K): check shortest path between current location and v in (v union K). 

		Graph<Vertex,Edge> kernel = GraphUtils.buildKernel(ctpGraph, actionState, true); 
		
		// First, we check if the kernel contains the goal vertex.
		Transformer<Edge, Double> cost = new Transformer<Edge, Double>() {
			@Override
			public Double transform(Edge e) {
				return e.getCost();
			}
		};
		
		DijkstraShortestPath<Vertex, Edge> dij = new DijkstraShortestPath<Vertex, Edge>(kernel, cost, true);
		for (Vertex vertex : kernel.getVertices()) {
			if (vertex.isGoal(ctpGraph)) {
				Double dist = (Double) dij.getDistance(actionState.getLocation(), vertex);
				if (dist != null) {
					List<Edge> shortestPath = dij.getPath(actionState.getLocation(), vertex);
					Action action = new Action(shortestPath,dist,vertex);
					actions.add(action);
					break;					
				}
			}
		}
		
		// now we check all the neighbors of the kernel.
		for (Vertex vertex : ctpGraph.getGraph().getVertices()) {
			if (kernel.containsVertex(vertex)) {
				continue;
			}
			Collection<Vertex> neighbors = ctpGraph.getGraph().getNeighbors(vertex);
			for (Vertex neighbor : neighbors) {
				if (kernel.containsVertex(neighbor)) {
					kernel.addVertex(vertex);
					Edge e = ctpGraph.getGraph().findEdge(vertex, neighbor);
					// BUG!!!!!!!
					// if e is blocked then continue.
					if (actionState.getBlockedEdges().contains(e)) {
						continue;
					}
					kernel.addEdge(e, vertex, neighbor);
					dij = new DijkstraShortestPath<Vertex, Edge>(kernel, cost, true);
					Double dist = (Double) dij.getDistance(actionState.getLocation(), vertex);
					if (dist != null) {
						List<Edge> shortestPath = dij.getPath(actionState.getLocation(), vertex);
						
						// Hack: we need to check that the path is not exceeding the
						// goal state.
						boolean isExceeding = false;
						if (!vertex.isGoal(ctpGraph)) {
							for (Edge edge : shortestPath) {
								if (ctpGraph.getGraph().getEndpoints(edge).contains(ctpGraph.getGoal())) {
									isExceeding = true;
									break;
								}
							}							
						}

						if (!isExceeding) {
							Action action = new Action(shortestPath,dist,vertex);
							actions.add(action);													
						}
					}
					kernel.removeVertex(vertex);
				}
			}
		}
		
		
		return actions;
	}
	
	public Vertex getDestination() {
		return destination;
	}

	public List<Edge> getPath() {
		return path;
	}

	@Override
	public String toString() {
		return this.path.get(0) + " " + destination.toString();
	}


}
